﻿// Copyright (c) 2009, Heiko Wundram (modelnine@h8me.eu).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
// conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
//   in the documentation and/or other materials provided with the distribution.
// * Neither the name of Heiko Wundram (h8me.eu) nor the names of its contributors may be used to endorse or promote products
//   derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Collections.Generic;
using eu.h8me.Parsing.ParserLib;

namespace eu.h8me.Parsing.LexerLib {

    // Regular expression class.
    public sealed class RegEx {

        // Match event.
        public delegate Token MatchEventType(LexState state, string data);

        // Data.
        private string _RegExString;
        private Tree _Tree;
        private RegExSetState _State;
        private object _Tag;
        private int? _Precedence;
        private int _SetPrecedence;
        private event MatchEventType _Event;

        // Constructor with set of named REs.
        internal RegEx(string re, char flags, RegExSetState state, Dictionary<string, Tree> namedre) {
            // Parse the regex.
            _RegExString = re;
            _Tree = RegExParser.Parse(re, flags, false, namedre);
            _State = state;
        }

        // Access regex string.
        public string RegExString {
            get {
                // Return regex.
                return _RegExString;
            }
        }

        // Tag accessor.
        public object Tag {
            get {
                // Return tag.
                return _Tag;
            }
            set {
                // Check whether state is finished.
                if( _State.IsFinished )
                    throw new ArgumentException("Trying to set RegEx tag when state finished");
                _Tag = value;
            }
        }

        // Match precedence.
        public int Precedence {
            get {
                // Return match precedence.
                if( !_State.IsFinished && !_Precedence.HasValue )
                    throw new ArgumentException("Precedence not set up");
                return _Precedence.GetValueOrDefault(_SetPrecedence);
            }
            set {
                // Check whether state is finished and set up.
                if( _State.IsFinished )
                    throw new ArgumentException("Trying to set RegEx precedence after state finish");
                else if( _Precedence < 0 )
                    throw new ArgumentException("Invalid precedence, must be positive");

                // Set up.
                _Precedence = value;
            }
        }

        // Set precedence.
        internal int SetPrecedence {
            get {
                // Return set precedence.
                return _SetPrecedence;
            }
            set {
                // Set up precedence.
                _SetPrecedence = value;
            }
        }

        // Event accessor.
        public event MatchEventType MatchEvent {
            add {
                // Check whether we're finished.
                if( _State.IsFinished )
                    throw new ArgumentException("Trying to add match event when state finished");
                _Event += value;
            }
            remove {
                // Check whether we're finished.
                if( _State.IsFinished )
                    throw new ArgumentException("Trying to remove match event when state finished");
                _Event -= value;
            }
        }

        // Match at newline flag.
        internal bool MatchAtNewLine {
            get {
                // Return newline match flag.
                return _Tree.MatchAtNewLine;
            }
        }

        // Tree accessor.
        internal Tree Tree {
            get {
                // Return tree.
                return _Tree;
            }
        }

        // Execute the match.
        internal Token Execute(RegExSet set, Grammar gram, RegExSetState state, Lexer lexer, Parser parser, int start, string data) {
            // Execute a match.
            if( _Event != null )
                return _Event(new LexState(set, gram, state, lexer, parser, this, start), data);

            // No match event bound, return that.
            return null;
        }

        // Stringification.
        public override string ToString() {
            // Return regex that constructed this.
            return _RegExString;
        }

        // Operator helper match.
        public static MatchEventType Token() {
            // Return operator helper.
            return delegate(LexState state, string data) { return state.Grammar[data] / data; };
        }

        // Token helper match.
        public static MatchEventType Token(string name) {
            // Return token helper.
            return delegate(LexState state, string data) { return state.Grammar[name] / data; };
        }

        // State push helper match.
        public static MatchEventType PushState(string target) {
            // Return goto helper.
            return delegate(LexState state, string data) { state.Lexer.PushState(target); return null; };
        }

        // State pop helper match.
        public static MatchEventType PopState() {
            // Return goto helper.
            return delegate(LexState state, string data) { state.Lexer.PopState(); return null; };
        }

        // Error match.
        public static MatchEventType Error(string msg) {
            // Signal error during lexing.
            return delegate(LexState state, string data) { throw new ArgumentException(msg); };
        }

    }

    // Lexer state.
    public sealed class LexState {

        // Data.
        private RegExSet _Set;
        private Grammar _Grammar;
        private RegExSetState _State;
        private Lexer _Lexer;
        private Parser _Parser;
        private RegEx _RegEx;
        private int _Start;

        // Constructor.
        internal LexState(RegExSet set, Grammar gram, RegExSetState state, Lexer lexer, Parser parser, RegEx regex, int start) {
            // Store data.
            _Set = set;
            _Grammar = gram;
            _State = state;
            _Lexer = lexer;
            _Parser = parser;
            _RegEx = regex;
            _Start = start;
        }

        // Access Regular expression set.
        public RegExSet Set {
            get {
                // Return set.
                return _Set;
            }
        }

        // Access grammar.
        public Grammar Grammar {
            get {
                // Return grammar.
                return _Grammar;
            }
        }

        // Access state.
        public RegExSetState State {
            get {
                // Return state.
                return _State;
            }
        }

        // Access lexer.
        public Lexer Lexer {
            get {
                // Return lexer.
                return _Lexer;
            }
        }

        // Access parser.
        public Parser Parser {
            get {
                // Return parser.
                return _Parser;
            }
        }

        // Access matching regex.
        public RegEx RegEx {
            get {
                // Return regex.
                if( _RegEx == null )
                    throw new ArgumentException("Trying to access RegEx for event that is not bound to one");
                return _RegEx;
            }
        }

        // Access current starting input position.
        public int Start {
            get {
                // Return start.
                return _Start;
            }
        }

    }

}
